#include "sublogic.h"
/*
    负责处理一些逻辑，和数据库打交道
*/

/*
    打印消息结构
*/
void printMsg(msgdata* msg){
    printf("_act: %d \n",msg->_act);
    printf("_acc: %s \n",msg->_acc);
    printf("_pwd: %s \n",msg->_pwd);
    printf("_isgroup: %d \n",msg->_isgroup);
    printf("_cancalstatue: %d \n",msg->_cancalstatue);
    printf("_who: %s \n",msg->_who);
    printf("_msg: %s \n",msg->_msg);
}


/*
    删除字符串的回车符
*/
void eraseStr_n(char* buf)
{
    char* p = buf;
    while(*p != '\n')	p++;
    *p = '\0';
}

/*
    打印时间戳
*/
char* printTime()
{
    time_t t;
    time(&t);               // 取得当前时间
    char* str = ctime(&t);  // 将日期转化为字符串格式
    eraseStr_n(str);
//    printf("%s\n",str);
    return str;
}

/*
    将用户注册信息写到数据库
*/
void userinfoTodatabase(char* acc,char* pwd)
{
    // 打开文件
    FILE* fp = fopen(USER_DATABASE,"a+");
    if(fp == NULL)
    {
        perror("fopen error!");
        exit(0);
    }
//    Fputs(acc,fp);
//    Fputs("\t",fp);
//    Fputs(pwd,fp);
//    Fputs("\t",fp);
//    Fputs("1",fp);
//    Fputs("\n",fp);

    fprintf(fp,"%s\t%s\t%s\n",acc,pwd,"1");

    fclose(fp);
    printf("\n# add a user info To Database!\n");
}


/*
    验证用户信息
*/
char checkUserInfo(char* acc,char* pwd)
{
    // 打开文件
    FILE* fp = fopen(USER_DATABASE,"r");
    if(fp == NULL)
    {
        perror("USER_DATABASE fopen error!");
        exit(0);
    }
    char userinfo[UINFO_LENGTH];    // 保存一条用户信息
    char temp_acc[USER_ACCOUNT];
    char temp_pwd[USER_ACCOUNT];
    char ingroup[1];

    // 遍历数据库文件，对比该帐号是否正确
    while(Fgets(userinfo,UINFO_LENGTH,fp) != NULL)
    {
//        sscanf (userinfo,"%s %s %s",temp_acc,temp_pwd,ingroup);
        sscanf (userinfo,"%s %s",temp_acc,temp_pwd);
//        printf("userinfo : %s,%s,%s\n",temp_acc,temp_pwd,ingroup);

        // 帐号存在
        if(strcmp(temp_acc,acc) == 0)
        {
            //printf("acc ok\n");
            // 密码正确
            if(strcmp(temp_pwd,pwd) == 0)
            {
                //printf("pwd ok\n");
                if(strcmp(ingroup,"0") == 0)    return 0;    // 不在群
                else                            return 1;    // 在群
            }
            // 密码错误
            else    return -2;
        }
    }
    // 帐号不存在
    return -1;
    fclose(fp);
}

/*
    注册帐号
        input : acc - 帐号；pwd - 密码 ;
        output: `0` - logon ok ; `-1` - acc error ; `-2` - pwd error ; `-3` - acc cover
*/
void logon(char* acc,char* pwd,int fd)
{
    msgdata msg;
    msg._act = _Logon;

    // acc format check
    if(strlen(acc) > USER_ACCOUNT)
    {
        msg._cancalstatue = -1;
    }
    // pwd format check
    else if(strlen(pwd) > USER_ACCOUNT)
    {
        msg._cancalstatue = -2;
    }
    else{
        // 验证用户是否存在
        char ret = checkUserInfo(acc,pwd);
        // 用不存在，则创建
        if(-1 == ret)
        {
            userinfoTodatabase(acc,pwd);
            msg._cancalstatue = 0;
        }
        // 帐号已存在
        else{
            msg._cancalstatue = -3;
        }
    }
    Write(fd, &msg,sizeof(msgdata));
}

/*
    登录服务器
        input : acc - 帐号；pwd - 密码 ;fd - sockfd ;
        output: `0` - login ok ; `-1` - acc error ; `-2` - pwd error
*/
void login(char* acc,char* pwd,int fd)
{
    int i = 0;
    msgdata msg;
    msg._act = _Login;
    strcpy(msg._acc,acc);

    // 验证密码
    char ret = checkUserInfo(acc,pwd);
    // 验证通过，加入在线列表
    if(ret == 0 || ret == 1)
    {
        addOnlineList(acc,fd,ret);
        ret = 0;
    }

    msg._cancalstatue = ret;
    Write(fd, &msg,sizeof(msgdata));
}

/*
     跟某人聊天
        input: name - 帐号
        output: `0` - send ok ; `-1` - send error ;
*/
void chatone(int fd,msgdata* msg)
{
    int who_fd = searchOnlineListFd(msg->_who);

    // 对方不在线 - 向发消息者报告
    if(-1 == who_fd)
    {
        msg->_cancalstatue = -1;
        Write(fd,msg,sizeof(msgdata));
        printf("# [Other no_line]");
    }
    // 对方在线 - 转发给对方
    else
    {
        msg->_cancalstatue = 0;
        Write(who_fd,msg,sizeof(msgdata));
        printf("# [Other on_line],");
    }

    // 服务器日志
    printf( "%s send %s : %s\n",msg->_acc,msg->_who,msg->_msg);
    saveChatRecord(msg->_acc,msg->_who,msg->_msg);// 保存聊天记录
}


/*
    群发消息
*/
char chatgroup(msgdata* msg)
{
    node* ptr = onlineList;

    while(ptr != NULL)
    {
        if(strcmp(ptr->ingroup,"1")==0) Write(ptr->sockfd,msg,sizeof(msgdata));
        ptr = ptr->next;
    }

    // 服务器日志
    printf( "# %s send All : %s\n",msg->_acc,msg->_msg);
    saveChatRecord(msg->_acc,msg->_who,msg->_msg);// 保存聊天记录
}

/*
    保存聊天记录到缓冲区
*/
void saveChatRecord(char* sender,char* receiver,char* msgcontent)
{
    static i = 0;
    // 缓冲区满了，写到 ‘数据库’
    if(i == CHAT_RECORDS)
    {
        // 将缓冲区聊天记录写入文件
        chatRecordTofile();
        i = 0;
    }

    // 构造一条聊天记录
    Achat* record = Malloc(sizeof(Achat));
    strcpy(record->_sender,sender);
    strcpy(record->_receiver,receiver);
    strcpy(record->_msgcontent,msgcontent);
    strcpy(record->_time,printTime());

    // 保存到缓冲区
    chatRecordList[i++] = record;
//    printf("# add a chat record\n");
}

/*
    将缓冲区聊天记录写入文件
*/
void chatRecordTofile()
{
    // 打开文件
    FILE* fp = fopen(CHAT_DATABASE,"a+");
    if(fp == NULL)
    {
        perror("CHAT_DATABASE fopen error!");
        exit(0);
    }

    // 遍历聊天记录缓冲表，依次写到数据库文件
    int j = 0;
    while(j < CHAT_RECORDS && chatRecordList[j] != NULL)
    {
        Fputs(chatRecordList[j]->_time,fp);
        Fputs("\t",fp);
        Fputs(chatRecordList[j]->_sender,fp);
        Fputs("\t",fp);
        Fputs(chatRecordList[j]->_receiver,fp);
        Fputs("\t",fp);
        Fputs(chatRecordList[j]->_msgcontent,fp);
        Fputs("\n",fp);
        j++;
    }
    fclose(fp);
    printf("\n# add %d chat record to file\n",j);
    // 重置缓冲区
    for(j = 0;j < CHAT_RECORDS;j++)
    {
        free(chatRecordList[j]);
        chatRecordList[j] = NULL;
    }
}


/*
    业务逻辑函数：处理用户 文件描述符上的请求
*/
void ClientMsgProcess(thFunctionArg* arg)
{
    printf("# ClientMsgProcess: \n");

    // 获取消息
    msgdata* msg = &arg->msg;
//    printMsg(msg);

    // 处理消息
    switch( msg->_act)
    {
        case _Logon: logon(msg->_acc,msg->_pwd,arg->sockfd); break;
        case _Login: login(msg->_acc,msg->_pwd,arg->sockfd); break;
        case _ChatOne: chatone(arg->sockfd,msg); break;
        case _ChatGroup: chatgroup(msg); break;
        case _JoinGroup: ; break;
        case _ExitGroup: ; break;
        default: printf("# Client Action Error\n"); break;
    }
    free(arg);
}


/*
    业务逻辑函数：处理监听 文件描述符上的 连接事件
*/
void acceptProcess(thFunctionArg* arg)
{
    printf("# acceptProcess: \n");

    struct sockaddr_in client_address;
    socklen_t client_addrlength = sizeof( client_address );

    // 获取新客户端地址
    int connfd = accept( arg->sockfd, ( struct sockaddr* )&client_address, &client_addrlength );

    // 加到 epoll 监听树
    addfd( arg->epollfd, connfd, true );
}
